﻿using Painter.Model;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace Painter.Component
{
    public enum Adorners
    {
        Rect,
        Lasso,
    }
    public abstract class AdornerControl
    {
        public static AAdorner GetAdorner(UIElement adornedElement)
        {
            AdornerLayer layer = AdornerLayer.GetAdornerLayer(adornedElement);
            IEnumerable<AAdorner> adorners = layer.GetAdorners(adornedElement)?.Cast<AAdorner>();
            return adorners?.First();
        }
        public static AAdorner CreateAdorner(UIElement adornedElement, Adorners adorners)
        {
            AdornerLayer layer = AdornerLayer.GetAdornerLayer(adornedElement);
            layer.GetAdorners(adornedElement)?.ToList()?.ForEach(o => layer.Remove(o));
            switch (adorners)
            {
                case Adorners.Lasso:
                    layer.Add(new LassoAdorner(adornedElement, new StreamGeometry()));break;
                case Adorners.Rect:
                    layer.Add(new RectAdorner(adornedElement, new RectangleGeometry())); break;
                default: break;
            }
            return layer.GetAdorners(adornedElement)?.Cast<AAdorner>().First();
        }
        public static void Clear(UIElement adornedElement)
        {
            AdornerLayer layer = AdornerLayer.GetAdornerLayer(adornedElement);
            layer.GetAdorners(adornedElement)?.ToList()?.ForEach(o => layer.Remove(o));
        }
    }
    public class AAdorner:Adorner
    {
        public Geometry mGeometry;
        public DrawingVisual mImage=new DrawingVisual();
        public UIElement mAdornedElement;
        public List<Point> mStroke = new List<Point>();
        public StrokeCollection mSelectedStroke, mUnSelectStrokes;
        public System.Windows.Point mLastPoint = new System.Windows.Point();
        protected Pen mRenderPen = new Pen(new SolidColorBrush(new Color()
            { A = 255, R = 0, G = 122, B = 204 }), 2) { DashStyle = DashStyles.Dash };

        public AAdorner(UIElement adornedElement) : base(adornedElement) {mAdornedElement=adornedElement; }
        public virtual void BeginUpdate(Point pt) { }
        public virtual void EndUpdate(DrawingVisual image) { mStroke.Clear(); }
        protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            CaptureMouse();
        }
        protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e)
        {
            ReleaseMouseCapture();
        }
        public DrawingVisual GetImage(UIElement adornedElement)
        {
            return mImage;
        }
        protected override void OnPreviewMouseMove(MouseEventArgs e)
        {
            Cursor = Cursors.SizeAll;
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                PaintCanvas canvas = (mAdornedElement as PaintCanvas);
				if (mSelectedStroke == null)
                {
                    if (mStroke.Count < 4 && mStroke.Count > 1)
                    {
                        Rect rt = new Rect(mStroke[0], mStroke[1]);
                        mSelectedStroke = new StrokeCollection(canvas.Strokes.SelectMany(o =>
                            o.GetClipResult(rt).ToArray()
                        ));
                        mUnSelectStrokes = new StrokeCollection(canvas.Strokes.SelectMany(o =>
                            o.GetEraseResult(rt).ToArray()
                        ));
                        canvas.Strokes = new StrokeCollection(mSelectedStroke.ToArray().Concat(mUnSelectStrokes));
                    }
                    else if (mStroke.Count > 3)
                    {
                        mSelectedStroke = new StrokeCollection(canvas.Strokes.SelectMany(o =>
                            o.GetClipResult(mStroke).ToArray()
                        ));
                        mUnSelectStrokes = new StrokeCollection(canvas.Strokes.SelectMany(o =>
                            o.GetEraseResult(mStroke).ToArray()
                        ));
                        canvas.Strokes = new StrokeCollection(mSelectedStroke.ToArray().Concat(mUnSelectStrokes));
                    }
                }
                System.Windows.Point pt = e.GetPosition(mAdornedElement);
				mSelectedStroke?.Transform(new Matrix(1,0,0,1,pt.X - mLastPoint.X,pt.Y - mLastPoint.Y ), false);
                Matrix matrix = RenderTransform.Value;
                ScaleData scale_data = ScaleData.Find_CreateScaleData(mAdornedElement);
                matrix.Translate((pt.X - mLastPoint.X)* scale_data.Scale, (pt.Y - mLastPoint.Y)* scale_data.Scale);
                RenderTransform = new MatrixTransform(matrix);
                if (mImage != null) mImage.Transform = new MatrixTransform(matrix);
            }
            mLastPoint = e.GetPosition(mAdornedElement);
        }
        protected override void OnRender(DrawingContext drawingContext)
        {
            drawingContext.PushClip(mGeometry);
            drawingContext.DrawDrawing(mImage?.Drawing);
            drawingContext.Pop();
            drawingContext.DrawGeometry(Brushes.Transparent, mRenderPen, mGeometry);
        }
    }
    public class LassoAdorner : AAdorner
    {
        public LassoAdorner(UIElement adornedElement, Geometry geometry) : base(adornedElement) { mGeometry=geometry; }
        public override void BeginUpdate(Point pt)
        {
            mStroke.Add(pt);
            if (mStroke.Count > 2)
            {
                using (StreamGeometryContext ctx = (mGeometry as StreamGeometry).Open())
                {
                    ctx.BeginFigure(mStroke.FirstOrDefault(), true, false);
                    ctx.PolyLineTo(mStroke.Skip(1).ToList(), true, true);
                }
            }
            InvalidateVisual();
        }
        public override void EndUpdate(DrawingVisual image)
        {
            using (StreamGeometryContext ctx = (mGeometry as StreamGeometry).Open())
            {
                ctx.BeginFigure(mStroke.FirstOrDefault(), true, true);
                ctx.PolyLineTo(mStroke.Skip(1).ToList(), true, true);
            }

            mImage.Copy(image);
            InvalidateVisual();
            base.EndUpdate(image);
        }
    }
    class RectAdorner : AAdorner
    {
        public RectAdorner(UIElement adornedElement, Geometry geometry) : base(adornedElement) {mGeometry = geometry; }
        public override void BeginUpdate(Point pt)
        {
            if (mStroke.Count < 2){
                mStroke.Add(pt);
            }
            else{
                mStroke[1] = pt;
                (mGeometry as RectangleGeometry).Rect = new Rect(mStroke[0], mStroke[1]);
            }
            InvalidateVisual();
        }
        public override void EndUpdate(DrawingVisual image)
        {
            if (mStroke.Count == 0 && !image.DescendantBounds.IsEmpty)
                (mGeometry as RectangleGeometry).Rect = new Rect(0, 0, image.DescendantBounds.Width, image.DescendantBounds.Height);

            mImage.Copy(image);
            InvalidateVisual();
            base.EndUpdate(image);
        }
    }
}
